﻿/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;

namespace CSharpKit.Numerics.LinearAlgebra.Solvers
{
    /// <summary>
    /// An iterator that is used to check if an iterative calculation should continue or stop.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public sealed class Iterator<T> : ICloneable<Iterator<T>>
        where T : struct, IFormattable, IEquatable<T>
    {
        #region Constructors

        public Iterator()
            : this(Matrix<T>.Builder.IterativeSolverStopCriteria()) { }

        public Iterator(IEnumerable<IIterationStopCriterion<T>> stopCriteria)
        {
            _stopCriterias = new List<IIterationStopCriterion<T>>(stopCriteria);
        }

        #endregion


        /// <summary>
        /// The collection that holds all the stop criteria and the flag indicating if they should be added
        /// to the child iterators.
        /// </summary>
        private readonly List<IIterationStopCriterion<T>> _stopCriterias;


        #region Status

        private IterationStatus _status = IterationStatus.Continue;
        public IterationStatus Status => _status;

        #endregion


        #region ICloneable<Iterator<T>>

        public Iterator<T> Clone()
        {
            return new Iterator<T>(_stopCriterias.Select(sc => sc.Clone()));
        }


        #endregion


        public void Cancel()
        {
            _status = IterationStatus.Cancelled;
        }

        public void Reset()
        {
            _status = IterationStatus.Continue;

            foreach (var stopCriterion in _stopCriterias)
            {
                stopCriterion.Reset();
            }
        }



        /// <summary>
        /// Determines the status of the iterative calculation based on the stop criteria stored
        /// by the current <see cref="Iterator{T}"/>. Result is set into <c>Status</c> field.
        /// </summary>
        /// <param name="iterationNumber">The number of iterations that have passed so far.(到目前为止已经通过的迭代次数)</param>
        /// <param name="solutionVector">The vector containing the current solution values.(包含当前解值的向量)</param>
        /// <param name="sourceVector">The right hand side vector.(右手边矢量)</param>
        /// <param name="residualVector">The vector containing the current residual vectors.(残留向量)</param>
        /// <remarks>
        /// The individual iterators may internally track the progress of the calculation based
        /// on the invocation of this method. Therefore this method should only be called if the
        /// calculation has moved forwards at least one step.
        /// </remarks>
        public IterationStatus DetermineStatus(int iterationNumber,
            Vector<T> solutionVector, Vector<T> sourceVector, Vector<T> residualVector)
        {
            if (_stopCriterias.Count == 0)
            {
                throw new ArgumentException(Resources.Numerics.StopCriterionMissing);
            }

            if (iterationNumber < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(iterationNumber));
            }

            // While we're cancelled we don't call on the stop-criteria.
            if (_status == IterationStatus.Cancelled)
            {
                return _status;
            }

            foreach (var stopCriterion in _stopCriterias)
            {
                var status = stopCriterion.DetermineStatus(iterationNumber, solutionVector, sourceVector, residualVector);
                if (status == IterationStatus.Continue)
                {
                    continue;
                }

                _status = status;
                return _status;
            }

            // Got all the way through
            // So we're running because we had vectors passed to us.
            _status = IterationStatus.Continue;

            return _status;
        }



        //}}@@@
    }


}

